package com.carey.blog.util;

import static com.carey.blog.model.Skin.SKINS;
import static com.carey.blog.model.Skin.SKIN_DIR_NAME;
import static com.carey.blog.model.Skin.SKIN_NAME;

import java.io.File;
import java.io.FileFilter;
import java.io.FileReader;
import java.io.IOException;
import java.util.HashSet;
import java.util.Properties;
import java.util.Set;
import java.util.logging.Level;
import java.util.logging.Logger;

import org.json.JSONArray;
import org.json.JSONException;
import org.json.JSONObject;

import com.carey.blog.framework.latke.util.freemarker.Templates;
import com.carey.blog.listener.CareyBlogServletListener;
import com.carey.blog.model.Preference;

/**
 * Skin utilities.
 * 
 */
public final class Skins {

	/**
	 * Time zone utilities.
	 */
	private TimeZones timeZoneUtils = TimeZones.getInstance();
	/**
	 * Logger.
	 */
	private static final Logger LOGGER = Logger
			.getLogger(Skins.class.getName());

	/**
	 * Loads skins for the specified preference.
	 * 
	 * @param preference
	 *            the specified preference
	 * @throws JSONException
	 *             json exception
	 */
	public void loadSkins(final JSONObject preference) throws JSONException {
		LOGGER.info("Loading skins....");

		final String skinDirName = preference.getString(SKIN_DIR_NAME);
		preference.put(SKIN_DIR_NAME, skinDirName);
		LOGGER.log(Level.INFO, "skin dir [name={0}]", skinDirName);

		final String skinName = getSkinName(skinDirName);
		preference.put(SKIN_NAME, skinName);
		LOGGER.log(Level.INFO, "Current skin[name={0}]", skinName);

		final Set<String> skinDirNames = getSkinDirNames();
		LOGGER.log(Level.FINER, "Loaded skins[dirNames={0}]", skinDirNames);
		final JSONArray skinArray = new JSONArray();
		for (final String dirName : skinDirNames) {
			final JSONObject skin = new JSONObject();
			skinArray.put(skin);

			final String name = getSkinName(dirName);
			skin.put(SKIN_NAME, name);
			skin.put(SKIN_DIR_NAME, dirName);
		}

		preference.put(SKINS, skinArray.toString());

		try {
			final String webRootPath = CareyBlogServletListener.getWebRoot();
			final String skinPath = webRootPath + SKINS + "/" + skinDirName;
			Templates.CONFIGURATION.setDirectoryForTemplateLoading(new File(
					skinPath));
		} catch (final IOException e) {
			throw new RuntimeException(e);
		}

		final String localeString = preference
				.getString(Preference.LOCALE_STRING);
		if ("zh_CN".equals(localeString)) {
			timeZoneUtils.setTimeZone("Asia/Shanghai");
		}

		LOGGER.info("Loaded skins end");
	}

	/**
	 * Gets all skin directory names. Scans the
	 * {@linkplain CareyBlogServletListener#getWebRoot() Web root}/skins/
	 * directory, using the subdirectory of it as the skin directory name, for
	 * example,
	 * 
	 * <pre>
	 * ${Web root}/skins/
	 *     <b>classic</b>/
	 *     <b>simple-art</b>/
	 * </pre>
	 * 
	 * @return a set of skin name, returns an empty set if not found
	 */
	public Set<String> getSkinDirNames() {
		final String webRootPath = CareyBlogServletListener.getWebRoot();
		final File webRoot = new File(webRootPath + "skins/");
		final File[] skinDirs = webRoot.listFiles(new FileFilter() {

			@Override
			public boolean accept(final File pathname) {
				return pathname.isDirectory() ? true : false;
			}
		});

		final Set<String> ret = new HashSet<String>();
		for (int i = 0; i < skinDirs.length; i++) {
			final File file = skinDirs[i];
			ret.add(file.getName());
		}

		return ret;
	}

	/**
	 * Gets the skin name for the specified skin directory name. The skin name
	 * was configured in skin.properties file({@code name} as the key) under
	 * skin directory specified by the given skin directory name.
	 * 
	 * @param skinDirName
	 *            the given skin directory name
	 * @return skin name, returns {@code null} if not found or error occurs
	 * @see #getSkinDirNames()
	 */
	public String getSkinName(final String skinDirName) {
		final String webRootPath = CareyBlogServletListener.getWebRoot();
		final File skins = new File(webRootPath + "skins/");
		final File[] skinDirs = skins.listFiles(new FileFilter() {

			@Override
			public boolean accept(final File pathname) {
				return pathname.isDirectory()
						&& pathname.getName().equals(skinDirName) ? true
						: false;
			}
		});

		if (null == skinDirs) {
			LOGGER.severe("Skin directory is null");

			return null;
		}

		if (1 != skinDirs.length) {
			LOGGER.log(Level.SEVERE, "Skin directory count[{0}]",
					skinDirs.length);

			return null;
		}

		try {
			final Properties ret = new Properties();
			final String skinPropsPath = skinDirs[0].getPath() + "/"
					+ "skin.properties";
			ret.load(new FileReader(skinPropsPath));

			return ret.getProperty("name");
		} catch (final Exception e) {
			LOGGER.log(Level.SEVERE, "Read skin configuration error[msg={0}]",
					e.getMessage());

			return null;
		}
	}

	/**
	 * Gets the {@link Skins} singleton.
	 * 
	 * @return the singleton
	 */
	public static Skins getInstance() {
		return SingletonHolder.SINGLETON;
	}

	/**
	 * Private default constructor.
	 */
	private Skins() {
	}

	/**
	 * Singleton holder.
	 * 
	 */
	private static final class SingletonHolder {

		/**
		 * Singleton.
		 */
		private static final Skins SINGLETON = new Skins();

		/**
		 * Private default constructor.
		 */
		private SingletonHolder() {
		}
	}
}
